package timing.ukulele.flow.api;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import timing.ukulele.flow.exception.NoUserException;
import timing.ukulele.flow.persistent.Flow;
import timing.ukulele.flow.service.FlowService;
import timing.ukulele.flow.service.OperateTaskService;
import timing.ukulele.flow.vo.ExecuteVO;
import timing.ukulele.flow.vo.TaskVO;
import timing.ukulele.common.ResponseResult;
import timing.ukulele.common.flow.FlowData;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.HistoryService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.persistence.entity.VariableInstance;
import org.activiti.engine.impl.persistence.entity.VariableInstanceEntityImpl;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/flow")
@Slf4j
public class FlowApi {

    @Autowired
    private OperateTaskService operateTaskService;

    private final FlowService flowService;

    private final TaskService taskService;

    private final HistoryService historyService;

    public FlowApi(FlowService flowService, TaskService taskService, HistoryService historyService) {
        this.flowService = flowService;
        this.taskService = taskService;
        this.historyService = historyService;
    }

    @GetMapping("/getById/{id}")
    @ApiOperation("根据id获取")
    public ResponseResult<FlowData> getById(@PathVariable("id") Long id) {
        final Flow flow = flowService.getById(id);
        if (flow != null) {
            FlowData data = new FlowData();
            BeanUtils.copyProperties(flow, data);
            return ResponseResult.success(data);
        }
        return ResponseResult.success();
    }

    @GetMapping("/instance/{id}")
    public ResponseResult<TaskVO> getInstance(@PathVariable("id") String id) {
        Task nextTask = taskService
                .createTaskQuery().processInstanceId(id).singleResult();
        if (nextTask != null) {
            TaskVO vo = new TaskVO();
            BeanUtils.copyProperties(nextTask, vo);
            return ResponseResult.success(vo);
        }
        return ResponseResult.success();
    }

    @GetMapping("/HistoryTasks/{instanceId}")
    public ResponseResult<List<TaskVO>> getHistoryTasksByInstanceId(@PathVariable("instanceId") String instanceId) {
        List<HistoricTaskInstance> hisList = historyService.createHistoricTaskInstanceQuery()
            .processInstanceId(instanceId)
            .orderByTaskCreateTime()
            .asc()
            .list();
        if (CollectionUtils.isEmpty(hisList)) {
            return ResponseResult.error("没有查到相关历史任务!");
        }
        List<TaskVO> list = new ArrayList<>();
        for (HistoricTaskInstance historicTaskInstance : hisList) {
            TaskVO vo = new TaskVO();
            BeanUtils.copyProperties(historicTaskInstance, vo);
            list.add(vo);
        }
        return ResponseResult.success(list);
    }

    @GetMapping("/getBySystemId/{systemId}")
    @ApiOperation("根据id获取")
    public ResponseResult<List<FlowData>> getBySystemId(@PathVariable("systemId") Long systemId) {
        List<FlowData> flowDatas = flowService.getBySystemId(systemId);
        return ResponseResult.success(flowDatas);
    }

    @GetMapping("/all")
    @ApiOperation("获取所有流程")
    public ResponseResult<List<FlowData>> all() {
        final List<Flow> list = flowService.list();
        if (!CollectionUtils.isEmpty(list)) {
            List<FlowData> dataList = new ArrayList<>(list.size());
            list.forEach(flow -> {
                FlowData data = new FlowData();
                BeanUtils.copyProperties(flow, data);
                dataList.add(data);
            });
            return ResponseResult.success(dataList);
        }
        return ResponseResult.success();
    }

    @PostMapping(path = "/startFirstComplete")
    @ApiOperation(value = "开始任务",
            notes = "开始任务，任务进入下一个节点。参数必传processKey(流程定义的key)，startUser(启动用户)和(nextAssignee(下步执行人)"
                    + "或uuid或departs或areacode或roles或isand或usertype或rulecode或userpara)和tenantId(租户id即系统id)，其他参数根据所配置的流程节点传。")
    public ResponseResult<String> start(@RequestBody ExecuteVO executeVO) {
        if (executeVO == null) {
            return ResponseResult.error("参数不能为空");
        }
        if (StringUtils.isEmpty(executeVO.getProcessKey()) || StringUtils.isEmpty(executeVO.getStartUser())
                || StringUtils.isEmpty(executeVO.getTenantId())) {
            return ResponseResult.error("参数中必须包含processKey和startUser和tenantId");
        }
        try {
            return ResponseResult.success(operateTaskService.startFirstComplete(executeVO));
            } catch (Exception e) {
                if (e instanceof NoUserException) {
                return ResponseResult.error("启动失败:" + e.getMessage());
            }
            log.error(e.getMessage());
            return ResponseResult.error("启动失败:" + e.getMessage());
        }
    }

    @PostMapping(path = "/publicCompleteTask")
    @ApiOperation(value = "公共的执行任务",
                  notes = "公共的执行任务，任务进入下一个节点。参数必传processInstanceId(流程实例id)，assignee(当前执行人)和(nextAssignee(下步执行人)"
                      + "或uuid或departs或areacode或roles或island或usertype或rulecode或userpara)和taskId(任务id)，其他参数根据所配置的流程节点传。")
    public ResponseResult publicCompleteTask(@RequestBody ExecuteVO executeVO) {
        long start = System.currentTimeMillis();
        log.info("公共的执行任务接口开始时间:" + start);
        if (executeVO == null) {
            return ResponseResult.error("参数不能为空");
        }
        if (StringUtils.isEmpty(executeVO.getProcessInstanceId()) || StringUtils.isEmpty(executeVO.getAssignee())
                || StringUtils.isEmpty(executeVO.getTaskId())) {
            return ResponseResult.error("参数中必须包含processInstanceId,assignee,taskId");
        }
        try {
            operateTaskService.publicCompleteTask(executeVO);
        } catch (Exception e) {
            if (e instanceof NoUserException) {
                return ResponseResult.error("启动失败:" + e.getMessage());
            }
            log.error(e.getMessage());
            return ResponseResult.error("执行失败:" + e.getMessage());
        }
        long end = System.currentTimeMillis();
        log.info("公共的执行任务接口结束时间:" + end);
        log.info("公共的执行任务接口用时：" + (end - start) / 1000 + "秒");
        return ResponseResult.success("执行成功!");
    }

    @PostMapping(path = "/findUndoTaskByAssignee")
    @ApiOperation(value = "根据流程assignee查询当前人的待办任务",
            notes = "根据流程assignee查询当前人的待办任务。返回值为包含taskID(任务ID),taskName(任务名称),taskCreateTime(任务的创建时间),taskAssignee(任务的办理人),processInstanceId(流程实例ID),executionId(执行对象ID),processDefinitionId(流程定义ID)字段的列表")
    public ResponseResult<Page<TaskVO>> findUnDoTaskByAssignee(
            @ApiParam("代理人（用户）") @RequestParam(value = "assignee", required = false) String assignee,
            @ApiParam("租户id") @RequestParam("tenantId") String tenantId,
            @ApiParam(value = "变量名") @RequestParam(value = "variableName", required = false) String variableName,
            @ApiParam(value = "变量值") @RequestParam(value = "variableValue", required = false) String variableValue,
            @ApiParam(value = "流程实例id") @RequestParam(value = "processInstanceId", required = false) String processInstanceId,
            @ApiParam(value = "创建时间排序方向:1代表时间升序、-1代表时间降序") @RequestParam("sort") Integer sort,
            @ApiParam(value = "当前页码", example = "1") @RequestParam("current") Integer current,
            @ApiParam(value = "每页大小", example = "10") @RequestParam("pageSize") Integer pageSize) {
        //创建任务查询对象
        List<Task> taskList;
        TaskQuery taskQuery;
        if (sort == 1) {
            taskQuery = taskService.createTaskQuery().taskTenantId(tenantId).orderByTaskCreateTime().asc();
        } else if (sort == -1) {
            taskQuery = taskService.createTaskQuery().taskTenantId(tenantId).orderByTaskCreateTime().desc();
        } else {
            //默认按升序
            taskQuery = taskService.createTaskQuery().taskTenantId(tenantId);
        }

        try {
            if (StringUtils.hasLength(assignee)) {
                taskQuery.taskCandidateOrAssigned(assignee);
            }
            if (StringUtils.hasLength(variableName) && StringUtils.hasLength(variableValue)) {
                taskQuery.taskVariableValueEquals(variableName, variableValue);
            }
            if (StringUtils.hasLength(processInstanceId)) {
                taskQuery.processInstanceId(processInstanceId);
            }
            taskList = taskQuery.listPage((current - 1) * pageSize, pageSize);
        } catch (Exception e) {
            log.error(e.getMessage());
            return ResponseResult.error("查询失败：" + e.getMessage());
        }
        if (!CollectionUtils.isEmpty(taskList)) {
            List<TaskVO> resultList = new ArrayList<>();
            for (Task task : taskList) {
                TaskVO vo = new TaskVO();
                if (StringUtils.isEmpty(task.getAssignee())) {
                    List<IdentityLink> identityLinksForTask =
                            taskService.getIdentityLinksForTask(task.getId());
                    if (!CollectionUtils.isEmpty(identityLinksForTask)) {
                        StringBuffer stringBuffer = new StringBuffer();
                        for (int i = 0; i < identityLinksForTask.size(); i++) {
                            if (!StringUtils.isEmpty(identityLinksForTask.get(i).getUserId())) {
                                if (i == identityLinksForTask.size() - 1) {
                                    stringBuffer.append(identityLinksForTask.get(i).getUserId());
                                } else {
                                    stringBuffer.append(identityLinksForTask.get(i).getUserId() + ",");
                                }
                                vo.setCandidate(stringBuffer.toString());
                            }
                        }
                    }
                }
                BeanUtils.copyProperties(task, vo);
                vo.setProperties(operateTaskService.findRunningTaskParams(task));
                Map<String, VariableInstance> variableInstances = taskService.getVariableInstances(task.getId());
                if (!CollectionUtils.isEmpty(variableInstances)) {
                    if (vo.getRunningVariables() == null) {
                        vo.setRunningVariables(new ArrayList<>(variableInstances.size()));
                    }
                    variableInstances.forEach((key, value) ->
                    {
                        vo.getRunningVariables().add((VariableInstanceEntityImpl) value);
                    });
                }
                vo.setTaskType(0);
                resultList.add(vo);
            }
            Page<TaskVO> page = new Page<>(current, pageSize);
            page.setRecords(resultList);
            page.setTotal(taskQuery.count());
            return ResponseResult.success(page);
        } else {
            return ResponseResult.success(null);
        }
    }

}
